#include "ckey.h"
#include <stdint.h>

#ifndef NULL
#define NULL ((void *)0)
#endif

#define KEY_STABLE_TIME(key) (key)->stable_time
#define MULTIPLE_CLICK_DURATION(key) (key)->multiple_click_duration
#define LONG_PRESS_DURATION(key) (key)->long_press_duration

static int key_scan_interval = DEFAULT_KEY_SCAN_INTERVAL;
static struct key_list node_head = {&node_head, &node_head};

static inline void set_even(key_handle_t *k, KEY_ENUM s)
{
    if (k->key_even) {
        k->key_even(k, s);
    }
}

static inline void set_status_with_even(key_handle_t *k, KEY_ENUM s)
{
    k->status = s;
    set_even(k, s);
}

static inline void set_status(key_handle_t *k, KEY_ENUM s)
{
    k->status = s;
}

void Key_Handler(void)
{
    for (key_handle_t *key = (key_handle_t *)node_head.next; key != (key_handle_t *)&node_head; key = (key_handle_t *)key->next) {
        if (!key->is_key_press)
            continue;

        switch (key->status) {
        case KEY_PRESS:
            if (key->is_key_press()) {
                key->even_tick++;
                if (key->even_tick >= (LONG_PRESS_DURATION(key) / key_scan_interval)) {
                    if (key->multiple_count == 0)
                        set_even(key, KEY_PRESS);
                    set_status_with_even(key, KEY_LONG_PRESS);
                }
            } else {
                if (key->even_tick >= (KEY_STABLE_TIME(key) / key_scan_interval)) {
                    key->even_tick = 0;
                    key->multiple_count++;
                    if (key->multiple_count == 1)
                        set_even(key, KEY_PRESS);
                    set_status(key, KEY_CLICK);
                } else {
                    if (key->multiple_count > 1) {
                        set_status_with_even(key, KEY_MULTIPLE_CLICK);
                        set_status_with_even(key, KEY_RELEASE);
                    } else if (key->multiple_count == 1) {
                        set_status_with_even(key, KEY_CLICK);
                        set_status_with_even(key, KEY_RELEASE);
                    } else {
                        set_status(key, KEY_RELEASE);
                    }
                }
            }
            break;

        case KEY_CLICK:
            key->even_tick++;
            if (key->is_key_press()) {
                key->even_tick = 1;
                set_status(key, KEY_PRESS);
            } else if (key->even_tick >= MULTIPLE_CLICK_DURATION(key) / key_scan_interval) {
                set_even(key, key->multiple_count > 1 ? KEY_MULTIPLE_CLICK : KEY_CLICK);
                set_status_with_even(key, KEY_RELEASE);
            }
            break;

        case KEY_LONG_PRESS:
            if (!key->is_key_press()) {
                set_status_with_even(key, KEY_RELEASE);
            }
            break;

        case KEY_RELEASE:
            if (key->is_key_press()) {
                key->even_tick = 1;
                key->multiple_count = 0;
                set_status(key, KEY_PRESS);
            }
            break;

        default:
            break;
        }
    }
}

void Key_SetInterval(int ms)
{
    if (ms > 0)
        key_scan_interval = ms;
}

int Key_GetMultipleCount(key_handle_t *handle)
{
    if (!handle)
        return 0;

    return handle->multiple_count;
}

void Key_InitSetting(key_handle_t *handle, key_press_func key_down_func, key_even_cb key_even_cb)
{
    if (!handle)
        return;
    handle->long_press_duration = DEFAULT_LONG_PRESS_DURATION;
    handle->stable_time = DEFAULT_KEY_STABLE_TIME;
    handle->multiple_click_duration = DEFAULT_MULTIPLE_CLICK_DURATION;
    handle->is_key_press = key_down_func;
    handle->key_even = key_even_cb;
}

void Key_Add(key_handle_t *handle)
{
    if (!handle)
        return;

    struct key_list *prev = (struct key_list *)node_head.prev;
    struct key_list *next = (struct key_list *)&node_head;

    next->prev = (struct key_list *)handle;
    handle->next = next;
    handle->prev = prev;
    prev->next = (struct key_list *)handle;
}

void Key_Remove(key_handle_t *handle)
{
    if (!handle)
        return;

    struct key_list *prev = handle->prev;
    struct key_list *next = handle->next;

    prev->next = next;
    next->prev = prev;

    handle->prev = NULL;
    handle->next = NULL;
}

bool Key_IsRelease(key_handle_t *handle)
{
    if (handle) {
        return !handle->is_key_press() && (handle->status == KEY_RELEASE);
    } else {
        for (key_handle_t *key = (key_handle_t *)node_head.next; key != (key_handle_t *)(&node_head); key = (key_handle_t *)key->next) {
            if (key->is_key_press() || (key->status != KEY_RELEASE))
                return false;
        }

        return true;
    }
}
